package com.fb.cvstool;

import hudson.org.apache.tools.ant.taskdefs.AbstractCvsTask;
import hudson.org.apache.tools.ant.taskdefs.cvslib.ChangeLogTask;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.apache.commons.io.FileUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.netbeans.lib.cvsclient.command.CommandAbortedException;
import org.netbeans.lib.cvsclient.command.CommandException;
import org.netbeans.lib.cvsclient.connection.AuthenticationException;

import com.fb.cvstool.netbeans.NetBeansCVSClient;

public class CvsLogProcessor {

	private static String dateFormat = "yyyyMMdd HH:mm:ss";
	private String sourceCvsRoot;
	private String sourceLocalPath;

	private String targetCvsRoot;

	public String getTargetCvsRoot() {
		return targetCvsRoot;
	}

	public void setTargetCvsRoot(String targetCvsRoot) {
		this.targetCvsRoot = targetCvsRoot;
	}

	public String getTargetLocalPath() {
		return targetLocalPath;
	}

	public void setTargetLocalPath(String targetLocalPath) {
		this.targetLocalPath = targetLocalPath;
	}

	private String targetLocalPath;

	private String propertyFileName;
	private Date startTime;

	private static String cvsChangesLogFile = "\\cvs-update-log.xml";
	private static String cvsOperationErrorLogFile = "\\cvs-update-log.xml";
	private static String cvsOperationLogFile = "\\cvs-log.txt";
	private Properties properties;

	private String fullCvsChangesLogFile;
	private String fullCvsOperationErrorLogFile;
	private String fullCvsOperationLogFile;

	public String getFullCvsOperationLogFile() {
		return fullCvsOperationLogFile;
	}

	public void setFullCvsOperationLogFile(String fullCvsOperationLogFile) {
		this.fullCvsOperationLogFile = fullCvsOperationLogFile;
	}

	public Properties getProperties() {
		return properties;
	}

	public void setProperties(Properties properties) {
		this.properties = properties;
	}

	public Date getStartTime() {
		return startTime;
	}

	public void setStartTime(Date startTime) {
		this.startTime = startTime;
	}

	public String getDateFormat() {
		return dateFormat;
	}

	public void setDateFormat(String dateFormat) {
		this.dateFormat = dateFormat;
	}

	public String getPropertyFileName() {
		return propertyFileName;
	}

	public String getSourceLocalPath() {
		return sourceLocalPath;
	}

	public void setSourceLocalPath(String localPath) {
		this.sourceLocalPath = localPath;
	}

	public String getSourceCvsRoot() {
		return sourceCvsRoot;
	}

	public void setSourceCvsRoot(String cvsroot) {
		this.sourceCvsRoot = cvsroot;
	}

	public static void main(String[] args) throws Exception {

		CvsLogProcessor cvsclient = new CvsLogProcessor();
		Properties p = cvsclient.getPropertyFile();
		cvsclient.setProperties(p);

		String sourcecvsroot = p.getProperty("sourcecvsroot");
		cvsclient.setSourceCvsRoot(sourcecvsroot);
		String sourcelocalpath = p.getProperty("sourcelocalpath");
		cvsclient.setSourceLocalPath(sourcelocalpath);
		String targetCvsRoot = p.getProperty("targetcvsroot");
		cvsclient.setTargetCvsRoot(targetCvsRoot);
		String targetLocalPath = p.getProperty("targetlocalpath");
		cvsclient.setTargetLocalPath(targetLocalPath);

		String tmpcheckoutdirectory = p.getProperty("tmpcheckoutdirrectory");
		String sourceBranch = p.getProperty("sourcebranch");

		cvsclient.setStartTime(new Date());
		cvsclient.setFullCvsChangesLogFile(cvsclient.getCurrentPath()
				+ cvsChangesLogFile);
		cvsclient.setFullCvsOperationErrorLogFile(cvsclient.getCurrentPath()
				+ cvsOperationErrorLogFile);
		cvsclient.setFullCvsOperationLogFile(cvsclient.getCurrentPath()
				+ cvsOperationLogFile);

		// query
		String lastExecteTimeStr = cvsclient.getLastExecutionTime(p);
		Date lastDate = stringToDate(lastExecteTimeStr);

		// find the changes in log file
		 //cvsclient.findChangesFromSpecialTime(lastDate, null);

		// XML parse it, and list all files!
		 List updatedFiles = cvsclient.parseChangeLogXML();

		// cvsclient.invokeCvsCommand();
		/*
		 * for (int i = 0; i < updatedFiles.size(); i++) { NodeBean bean =
		 * (NodeBean) updatedFiles.get(i); System.out.println("file:" +
		 * bean.getFileFullName()); }
		 */

		// cvsclient.saveExecutionTime(p);
		// String lastExecutionTime = cvsclient.getLastExecutionTime(p);

		// copy the source file ?

		// copy the target CVS
		// check whether the file existed or update
/*
		 //	String file1 = "CVS_repository/root/WebRoot/WEB-INF/web2.xml";
//		String file2 = "CVS_repository/root/WebRoot/WEB-INF/testdir/empty.txt";
//		String file3 = "CVS_repository/root/WebRoot/WEB-INF/lib/jakarta-oro.jar";
//		String file4 = "CVS_repository/root/WebRoot/WEB-INF/lib/antlr.jar";

		ArrayList<String> files = new ArrayList<String>();
		files.add(file1);
//		files.add(file2);
//		files.add(file3);
//		files.add(file4);

		HashMap<String, String> resultmap = cvsclient.checkCVSFileStatus(files,
				targetCvsRoot, targetLocalPath);

		System.out.println("----pay more attention to the deleted files!");

		cvsclient.clearTempDirectory(tmpcheckoutdirectory);

		cvsclient.synchronizeFilesToLocalCVS(resultmap, targetCvsRoot,
				targetLocalPath, tmpcheckoutdirectory, sourceBranch);
		cvsclient.sendOutBackupFiles();
*/
	}

	public int clearTempDirectory(String tmpcheckoutdirrectory) {
		try {
			FileUtils.cleanDirectory(new File(tmpcheckoutdirrectory));
		} catch (IOException e) {
			e.printStackTrace();
			return -1;
		}
		return 0;
	}

	public void synchronizeFilesToLocalCVS(HashMap resultmap, String cvsroot,
			String localPath, String tmpdir, String sourceBranch) {
		// get file status
		Iterator keyItr = resultmap.keySet().iterator();
		while (keyItr.hasNext()) {
			String fName = (String) keyItr.next();
			String fStatue = (String) resultmap.get(fName);

			int fs = Integer.parseInt(fStatue);

			switch (fs) {
			case NetBeansCVSClient.STATUS_LOCALLY_MODIFIED:
				System.out.println("LOCALLY_MODIFIED");
			case NetBeansCVSClient.STATUS_LOCALLY_ADDED:
				System.out.println("STATUS_LOCALLY_ADDED");
			case NetBeansCVSClient.STATUS_LOCALLY_REMOVED:
				System.out.println("STATUS_LOCALLY_REMOVED");
			case NetBeansCVSClient.STATUS_NEEDS_MERGE:
				System.out.println("STATUS_NEEDS_MERGE");
			case NetBeansCVSClient.STATUS_UNRESOLVED_CONFLICT:
				System.out.println("STATUS_UNRESOLVED_CONFLICT");
			case NetBeansCVSClient.STATUS_INVALID:
				System.out.println("STATUS_INVALID");
			case NetBeansCVSClient.STATUS_HAS_CONFLICTS:
				System.out.println("STATUS_HAS_CONFLICTS");

			case NetBeansCVSClient.STATUS_UP_TO_DATE:
				System.out.println("update to Date");
				// copy & commit
				copyCommit(cvsroot, localPath, fName, sourceCvsRoot, tmpdir,
						sourceBranch, false);

			case NetBeansCVSClient.STATUS_NEEDS_CHECKOUT:
				System.out.println("STATUS_NEEDS_CHECKOUT");
				// checkout copy commit
				checkoutFile(cvsroot, localPath, fName);
				// copy & commit

				copyCommit(cvsroot, localPath, fName, sourceCvsRoot, tmpdir,
						sourceBranch, false);
			case NetBeansCVSClient.STATUS_NEEDS_PATCH:
				System.out.println("STATUS_NEEDS_PATCH");
				// checkout copy commit
				checkoutFile(cvsroot, localPath, fName);
				// copy & commit
				copyCommit(cvsroot, localPath, fName, sourceCvsRoot, tmpdir,
						sourceBranch, false);
			case NetBeansCVSClient.STATUS_UNKNOWN:
				System.out.println("STATUS_UNKNOWN");
				// copy & commit
				copyCommit(cvsroot, localPath, fName, sourceCvsRoot, tmpdir,
						sourceBranch, true);

			default:
				System.out.println("update to Date");
			}
		}

		// backup
	}

	public int checkoutFile(String cvsroot, String localPath, String fName) {
		int result = 0;
		try {
			CVSClient cvsclient = new CVSClient(cvsroot);
			cvsclient.openConnection();
			cvsclient.setLocalPath(localPath);
			cvsclient.excute(cvsclient.update(new String[] { fName }));
			cvsclient.closeConnection();
		} catch (IOException e) {
			e.printStackTrace();
			result = -1;
		} catch (CommandException e) {
			e.printStackTrace();
			result = -1;
		} catch (AuthenticationException e) {
			e.printStackTrace();
			result = -1;
		}
		return result;
	}

	// copy & commit
	public int copyCommit(String targetCvsRoot, String targetLocalPath,
			String fName, String sourceCvsRoot, String tempCheckoutDirectory,
			String branch, boolean isNew) {
		int result = 0;
		try {
			CVSClient cvsclient = new CVSClient(targetCvsRoot);
			cvsclient.openConnection();
			cvsclient.setLocalPath(targetLocalPath);

			checkoutCopyToTargetFromTempdir(sourceCvsRoot, targetLocalPath,
					tempCheckoutDirectory, fName, branch);
			
			if (isNew) {
				cvsclient.excute(cvsclient.add(new String[] { fName }));
				cvsclient.closeConnection();
				cvsclient.openConnection();
			}
			
			System.out.println("targetCvsRoot" + targetCvsRoot);
			System.out.println("targetLocalPath" + targetLocalPath);
			System.out.println(" why the file path was changed:" );
			cvsclient.excute(cvsclient.commit(new String[] { fName }));
			cvsclient.closeConnection();
		} catch (IOException e) {
			e.printStackTrace();
			result = -1;
		} catch (CommandException e) {
			e.printStackTrace();
			result = -1;
		} catch (AuthenticationException e) {
			e.printStackTrace();
			result = -1;
		}
		return result;
	}

	public int checkoutCopyToTargetFromTempdir(String sourceCvsRoot,
			String targetLocalPath, String tempCheckoutDirectory, String fName,
			String branch) {
		// 1 checkout to temp dir
		int result = 0;
		try {
			CVSClient cvsclient = new CVSClient(sourceCvsRoot);
			cvsclient.openConnection();
			cvsclient.setLocalPath(tempCheckoutDirectory);
			if (fName.startsWith("/")) {
				fName = fName.substring(1);
			}
			System.out.println("ignore unknow file !");
			cvsclient.excute(cvsclient.checkout(fName, branch));
			cvsclient.closeConnection();
		} catch (IOException e) {
			e.printStackTrace();
			result = -1;
		} catch (CommandException e) {
			e.printStackTrace();
			result = -1;
		} catch (AuthenticationException e) {
			e.printStackTrace();
			result = -1;
		}

		// 2. copy to target dir
		copyFile(tempCheckoutDirectory, targetLocalPath, fName);

		return result;
	}

	/**
	 * copyFile
	 * 
	 * @param tempCheckoutDirectory
	 * @param targetLocalPath
	 * @param fName
	 * @return int
	 */
	public int copyFile(String tempCheckoutDirectory, String targetLocalPath,
			String fName) {

		if (!fName.startsWith("/")) {
			fName = "/" + fName;
		}

		File sourceFile = new File(tempCheckoutDirectory + fName);

		String targetFile = targetLocalPath + fName;
		if( targetFile.lastIndexOf("/")>0) {
			targetFile = targetFile.substring(0, targetFile.lastIndexOf("/"));
			System.out.println("targetFile directory:" + targetFile);
		}

		try {
			FileUtils.copyFileToDirectory(sourceFile, new File(targetFile));
		} catch (IOException e) {
			e.printStackTrace();
			return -1;
		}
		return 0;
	}

	public void sendOutBackupFiles() {

		System.out.println("only send out email attached the file list!");

	}

	public HashMap<String, String> checkCVSFileStatus(List<String> files,
			String cvsroot, String localPath) throws CommandAbortedException,
			CommandException, AuthenticationException, IOException {
		HashMap<String, String> resultMap = new HashMap<String, String>();

		// CVSClient cvsclient = new CVSClient(cvsroot);
		// cvsclient.setLocalPath(localPath);

		System.out.println("cvsroot: " + cvsroot);
		System.out.println("localPath: " + localPath);

		NetBeansCVSClient netbeansCVSClient = new NetBeansCVSClient(cvsroot,
				localPath);
		String[] filesArr = new String[files.size()];
		Iterator itr = files.iterator();
		int i = 0;
		while (itr.hasNext()) {
			String fn = (String) itr.next();
			filesArr[i] = fn;
			i++;
		}

		System.out.println("checkStatus   netbeansCVSClient need label !");

		for (String fileStr : filesArr) {
			System.out.println("filesArr: " + fileStr);
			if (!fileStr.startsWith("/")) {
				fileStr = "/"+ fileStr;
			}
			int fileStatus = netbeansCVSClient.checkStatus(fileStr);
			resultMap.put(fileStr, String.valueOf(fileStatus));
		}

		return resultMap;
	}

	public List<NodeBean> parseChangeLogXML() throws DocumentException {
		// dom4j
		SAXReader reader = new SAXReader();
		System.out.println("--getFullCvsChangesLogFile:"
				+ getFullCvsChangesLogFile());
		Document document = reader.read(new File(getFullCvsChangesLogFile()));
		Element rootElm = document.getRootElement();
		System.out.println(rootElm.getName());
		List<Element> nodes = rootElm.elements("entry");
		List<NodeBean> updateFileList = new ArrayList<NodeBean>();
		for (Iterator<Element> it = nodes.iterator(); it.hasNext();) {
			Element elm = (Element) it.next();
			NodeBean bean = new NodeBean();
			bean = bean.convertElementToBean(elm);
			updateFileList.add(bean);
		}
		return updateFileList;

		// XPath
		/*
		 * try { Document doc = reader.read(new
		 * File(getFullCvsChangesLogFile())); List projects =
		 * doc.selectNodes("ReturnInfo/userlist/item"); Iterator it =
		 * projects.iterator(); while (it.hasNext()) { Element elm = (Element)
		 * it.next(); System.out.println("index:" + elm.attributeValue("index")
		 * + " level:" + elm.attributeValue("level") + " nickname:" +
		 * elm.attributeValue("nickname") + " country:" +
		 * elm.attributeValue("country") + " weiwang:" +
		 * elm.attributeValue("weiwang")); }
		 * 
		 * } catch (Exception ex) { ex.printStackTrace(); }
		 */

		// list File programer time

	}

	public String getFullCvsChangesLogFile() {
		return fullCvsChangesLogFile;
	}

	public void setFullCvsChangesLogFile(String fullCvsChangesLogFile) {
		this.fullCvsChangesLogFile = fullCvsChangesLogFile;
	}

	public String getFullCvsOperationErrorLogFile() {
		return fullCvsOperationErrorLogFile;
	}

	public void setFullCvsOperationErrorLogFile(
			String fullCvsOperationErrorLogFile) {
		this.fullCvsOperationErrorLogFile = fullCvsOperationErrorLogFile;
	}

	public String getLastExecutionTime(Properties p) {
		return p.getProperty("lastExecuteTime");
	}

	public void saveExecutionTime(Properties p) throws IOException {
		Date dt = this.getStartTime();
		String strtime = dateToString(dt);
		p.setProperty("lastExecuteTime", strtime);
		String propertyFile = getPropertyFileName();
		FileOutputStream fom = new FileOutputStream(new File(propertyFile));
		p.store(fom, null);
	}

	public static String dateToString(Date time) {
		SimpleDateFormat formatter;
		formatter = new SimpleDateFormat(dateFormat);
		String ctime = formatter.format(time);
		return ctime;
	}

	public static Date stringToDate(String strTime) throws ParseException {
		SimpleDateFormat formatter;
		formatter = new SimpleDateFormat(dateFormat);
		Date dt = formatter.parse(strTime);
		return dt;
	}

	private void findChangesFromSpecialTime(Date startDate, String branch)
			throws FileNotFoundException, ParseException {
		ChangeLogTask task = new ChangeLogTask();
		task.setCvsRoot(this.getSourceCvsRoot());
		task.setDir(new File(this.getSourceLocalPath()));
		task.setStart(startDate);

		// task.setDaysinpast(5);

		// output
		task.setBranch(branch);
		task.setDeststream(new FileOutputStream(getFullCvsChangesLogFile()));
		task.setError(new File(getFullCvsOperationErrorLogFile()));
		task.execute();

		System.out.println("End!");

	}

	private void invokeCvsCommand() throws FileNotFoundException {

		CvsTask task = new CvsTask();
		task.setCvsRoot(this.getSourceCvsRoot());
		task.setDest(new File(this.getSourceLocalPath()));

		task.setCommand("update");

		// output
		task.setOutput(new File(getFullCvsOperationLogFile()));
		task.setError(new File(getFullCvsOperationErrorLogFile()));

		task.execute();
	}

	public String getCurrentPath() {
		return System.getProperty("user.dir");
	}

	public void setPropertyFileName(String currentPath) {
		this.propertyFileName = currentPath + "/" + "config.properties";
	}

	public Properties getPropertyFile() {
		Properties p = new Properties();
		InputStream fs;
		this.setPropertyFileName(getCurrentPath());
		try {
			fs = new FileInputStream(new File(this.getPropertyFileName()));
			p.load(fs);
			fs.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return p;
	}

	private static class CvsTask extends AbstractCvsTask {

	}
}
